package com.lframework.xingyun.basedata.impl.product;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.github.pagehelper.PageInfo;
import com.lframework.starter.common.exceptions.impl.DefaultClientException;
import com.lframework.starter.common.utils.Assert;
import com.lframework.starter.common.utils.CollectionUtil;
import com.lframework.starter.common.utils.NumberUtil;
import com.lframework.starter.common.utils.ObjectUtil;
import com.lframework.starter.common.utils.StringUtil;
import com.lframework.starter.web.core.impl.BaseMpServiceImpl;
import com.lframework.starter.web.core.components.resp.PageResult;
import com.lframework.starter.web.core.utils.EnumUtil;
import com.lframework.starter.web.core.utils.IdUtil;
import com.lframework.starter.web.core.utils.JsonUtil;
import com.lframework.starter.web.core.utils.PageHelperUtil;
import com.lframework.starter.web.core.utils.PageResultUtil;
import com.lframework.starter.web.inner.service.GenerateCodeService;
import com.lframework.xingyun.basedata.bo.product.info.ProductInfoBo;
import com.lframework.xingyun.basedata.dto.product.ProductSelectorDto;
import com.lframework.xingyun.basedata.dto.product.ProductSpecDto;
import com.lframework.xingyun.basedata.entity.*;
import com.lframework.xingyun.basedata.enums.BaseDataOpLogType;
import com.lframework.xingyun.basedata.enums.ColumnType;
import com.lframework.xingyun.basedata.enums.ProductCategoryNodeType;
import com.lframework.xingyun.basedata.enums.ProductType;
import com.lframework.xingyun.basedata.mappers.ProductMapper;
import com.lframework.xingyun.basedata.service.make.MakeMethodMaterialService;
import com.lframework.xingyun.basedata.service.product.*;
import com.lframework.xingyun.basedata.service.unit.UnitService;
import com.lframework.xingyun.basedata.vo.product.info.*;
import com.lframework.xingyun.basedata.vo.product.property.realtion.CreateProductPropertyRelationVo;
import com.lframework.xingyun.basedata.vo.product.purchase.CreateProductPurchaseVo;
import com.lframework.xingyun.basedata.vo.product.purchase.UpdateProductPurchaseVo;
import com.lframework.xingyun.basedata.vo.product.retail.CreateProductRetailVo;
import com.lframework.xingyun.basedata.vo.product.retail.UpdateProductRetailVo;
import com.lframework.xingyun.basedata.vo.product.sale.CreateProductSaleVo;
import com.lframework.xingyun.basedata.vo.product.sale.UpdateProductSaleVo;
import com.lframework.starter.web.core.annotations.oplog.OpLog;
import com.lframework.starter.web.inner.service.RecursionMappingService;
import com.lframework.starter.web.core.utils.OpLogUtil;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ProductServiceImpl extends BaseMpServiceImpl<ProductMapper, Product> implements
    ProductService {

  @Autowired
  private ProductPurchaseService productPurchaseService;

  @Autowired
  private ProductSaleService productSaleService;

  @Autowired
  private ProductRetailService productRetailService;

  @Autowired
  private RecursionMappingService recursionMappingService;

  @Autowired
  private ProductPropertyService productPropertyService;

  @Autowired
  private ProductPropertyItemService productPropertyItemService;

  @Autowired
  private ProductPropertyRelationService productPropertyRelationService;

  @Autowired
  private ProductBundleService productBundleService;

  @Autowired
  private ProductAssistantUnitService productAssistantUnitService;

  @Autowired
  private MakeMethodMaterialService makeMethodMaterialService;

  @Override
  public PageResult<Product> query(Integer pageIndex, Integer pageSize, QueryProductVo vo) {
    Assert.greaterThanZero(pageIndex);
    Assert.greaterThanZero(pageSize);

    PageHelperUtil.startPage(pageIndex, pageSize);
    List<Product> datas = this.query(vo);

    return PageResultUtil.convert(new PageInfo<>(datas));
  }

  @Override
  public List<Product> query(QueryProductVo vo) {
    String shopId = vo.getShopId();
    if (StrUtil.isBlank(shopId)) {
        vo.setShopId(null);
    }
    return getBaseMapper().query(vo);
  }

  @Override
  public PageResult<ProductSelectorDto> selector(Integer pageIndex, Integer pageSize,
      QueryProductSelectorVo vo) {

    Assert.greaterThanZero(pageIndex);
    Assert.greaterThanZero(pageSize);

    String shopId = vo.getShopId();
    if (StrUtil.isBlank(shopId)) {
        vo.setShopId(null);
    }

    PageHelperUtil.startPage(pageIndex, pageSize);
    List<ProductSelectorDto> datas = getBaseMapper().selector(vo);

    return PageResultUtil.convert(new PageInfo<>(datas));
  }

  @Override
  public Integer queryCount(QueryProductVo vo) {

    return getBaseMapper().queryCount(vo);
  }

  @Cacheable(value = Product.CACHE_NAME, key = "@cacheVariables.tenantId() + #id", unless = "#result == null")
  @Override
  public Product findById(String id) {

    return getBaseMapper().selectById(id);
  }

  @Override
  public ProductSelectorDto findSelectorDtoById(String id) {
      return Optional.ofNullable(getBaseMapper().findSelectorDtoByIds(Collections.singletonList(id)))
              .filter(list -> !CollectionUtil.isEmpty(list))
              .map(list -> list.get(0)).orElse(null);
  }

  @Override
  public List<ProductSelectorDto> findSelectorDtoByIds(List<String> productIds) {

    return getBaseMapper().findSelectorDtoByIds(productIds);
  }

  @Override
  public List<String> getIdNotInProductProperty(String propertyId) {

    return getBaseMapper().getIdNotInProductProperty(propertyId);
  }

  @Override
  public List<String> getIdByCategoryId(String categoryId) {

    return getBaseMapper().getIdByCategoryId(categoryId);
  }

  @OpLog(type = BaseDataOpLogType.class, name = "停用商品，ID：{}", params = "#ids", loopFormat = true)
  @Transactional(rollbackFor = Exception.class)
  @Override
  public void batchUnable(Collection<String> ids) {

    if (CollectionUtil.isEmpty(ids)) {
      return;
    }

    Wrapper<Product> updateWrapper = Wrappers.lambdaUpdate(Product.class)
        .set(Product::getAvailable, Boolean.FALSE).in(Product::getId, ids);
    getBaseMapper().update(updateWrapper);
  }

  @OpLog(type = BaseDataOpLogType.class, name = "启用商品，ID：{}", params = "#ids", loopFormat = true)
  @Transactional(rollbackFor = Exception.class)
  @Override
  public void batchEnable(Collection<String> ids) {

    if (CollectionUtil.isEmpty(ids)) {
      return;
    }

    Wrapper<Product> updateWrapper = Wrappers.lambdaUpdate(Product.class)
        .set(Product::getAvailable, Boolean.TRUE).in(Product::getId, ids);
    getBaseMapper().update(updateWrapper);
  }

  @OpLog(type = BaseDataOpLogType.class, name = "新增商品，ID：{}, 编号：{}", params = {"#_result",
      "#vo.code"}, autoSaveParams = true)
  @Transactional(rollbackFor = Exception.class)
  @Override
  public String create(CreateProductVo vo) {
      if (vo.getProductType() == 3) {
          return createProductSpec(vo);
      }

    String shopId = vo.getShopId();

    Wrapper<Product> checkWrapper = Wrappers.lambdaQuery(Product.class)
        .eq(Product::getCode, vo.getCode())
        .eq(StrUtil.isNotEmpty(shopId), Product::getShopId, shopId)
        .isNull(StrUtil.isBlank(shopId), Product::getShopId);
    if (getBaseMapper().selectCount(checkWrapper) > 0) {
      throw new DefaultClientException("编号重复，请重新输入！");
    }

    checkWrapper = Wrappers.lambdaQuery(Product.class).eq(Product::getSkuCode, vo.getSkuCode())
            .eq(StrUtil.isNotEmpty(shopId), Product::getShopId, shopId)
            .isNull(StrUtil.isBlank(shopId), Product::getShopId);
    if (getBaseMapper().selectCount(checkWrapper) > 0) {
        throw new DefaultClientException("商品SKU编号重复，请重新输入！");
    }

    if (!StrUtil.isNotEmpty(shopId)) {
        vo.setShopId(null);
    }

    Product data = new Product();
    data.setId(IdUtil.getId());
    data.setCode(vo.getCode());
    data.setName(vo.getName());
    if (StringUtil.isNotBlank(vo.getShortName())) {
      data.setShortName(vo.getShortName());
    }
    data.setSkuCode(vo.getSkuCode());
    if (StringUtil.isNotBlank(vo.getExternalCode())) {
      data.setExternalCode(vo.getExternalCode());
    }

    data.setBrandId(vo.getBrandId());
    data.setCategoryId(vo.getCategoryId());

    if (StringUtil.isNotBlank(vo.getSpec())) {
      data.setSpec(vo.getSpec());
    }

    if (StringUtil.isNotBlank(vo.getUnit())) {
      data.setUnit(vo.getUnit());
    }

    if (StringUtil.isNotBlank(vo.getStockUnit())) {
      data.setStockUnit(vo.getStockUnit());
    }

    if (StringUtil.isNotBlank(vo.getSalesUnit())) {
      data.setSalesUnit(vo.getSalesUnit());
    }

    if (StringUtil.isNotBlank(vo.getRetailUnit())) {
      data.setRetailUnit(vo.getRetailUnit());
    }

    data.setProductType(EnumUtil.getByCode(ProductType.class, vo.getProductType()));
    data.setTaxRate(vo.getTaxRate());
    data.setSaleTaxRate(vo.getSaleTaxRate());
    data.setWeight(vo.getWeight());
    data.setVolume(vo.getVolume());

    data.setAvailable(Boolean.TRUE);
    data.setImageUrl(vo.getImageUrl());
    data.setIntroduction(vo.getIntroduction());
    data.setExpiryDay(vo.getExpiryDay());
    data.setShopId(vo.getShopId());

    getBaseMapper().insert(data);

    // 组合商品
    if (data.getProductType() == ProductType.BUNDLE) {
      if (CollectionUtil.isEmpty(vo.getProductBundles())) {
        throw new DefaultClientException("单品数据不能为空！");
      }

      BigDecimal salePrice = vo.getProductBundles().stream().map(
          productBundleVo -> NumberUtil.mul(productBundleVo.getBundleNum(),
              productBundleVo.getSalePrice())).reduce(NumberUtil::add).orElse(BigDecimal.ZERO);
      if (!NumberUtil.equal(vo.getSalePrice(), salePrice)) {
        throw new DefaultClientException("单品的销售价设置错误！");
      }

      BigDecimal retailPrice = vo.getProductBundles().stream().map(
          productBundleVo -> NumberUtil.mul(productBundleVo.getBundleNum(),
              productBundleVo.getRetailPrice())).reduce(NumberUtil::add).orElse(BigDecimal.ZERO);
      if (!NumberUtil.equal(vo.getRetailPrice(), retailPrice)) {
        throw new DefaultClientException("单品的零售价设置错误！");
      }

      List<ProductBundle> productBundles = vo.getProductBundles().stream().map(productBundleVo -> {
        ProductBundle productBundle = new ProductBundle();
        productBundle.setId(IdUtil.getId());
        productBundle.setMainProductId(data.getId());
        productBundle.setProductId(productBundleVo.getProductId());
        productBundle.setBundleNum(productBundleVo.getBundleNum());
        productBundle.setSalePrice(productBundleVo.getSalePrice());
        productBundle.setRetailPrice(productBundleVo.getRetailPrice());

        return productBundle;
      }).collect(Collectors.toList());

      productBundleService.saveBatch(productBundles);
    }

    if (vo.getPurchasePrice() == null) {
      throw new DefaultClientException("采购价不能为空！");
    }

    if (vo.getPurchasePrice().doubleValue() < 0D) {
      throw new DefaultClientException("采购价不允许小于0！");
    }

    CreateProductPurchaseVo createProductPurchaseVo = new CreateProductPurchaseVo();
    createProductPurchaseVo.setId(data.getId());
    createProductPurchaseVo.setPrice(vo.getPurchasePrice());

    productPurchaseService.create(createProductPurchaseVo);

    if (vo.getSalePrice() == null) {
      throw new DefaultClientException("销售价不能为空！");
    }

    if (vo.getSalePrice().doubleValue() < 0D) {
      throw new DefaultClientException("销售价不允许小于0！");
    }

    CreateProductSaleVo createProductSaleVo = new CreateProductSaleVo();
    createProductSaleVo.setId(data.getId());
    createProductSaleVo.setPrice(vo.getSalePrice());

    productSaleService.create(createProductSaleVo);

    if (vo.getRetailPrice() == null) {
      throw new DefaultClientException("零售价不能为空！");
    }

    if (vo.getRetailPrice().doubleValue() < 0D) {
      throw new DefaultClientException("零售价不允许小于0！");
    }

    CreateProductRetailVo createProductRetailVo = new CreateProductRetailVo();
    createProductRetailVo.setId(data.getId());
    createProductRetailVo.setPrice(vo.getRetailPrice());

    productRetailService.create(createProductRetailVo);

    if (!CollectionUtil.isEmpty(vo.getProperties())) {
      // 商品和商品属性的关系
      for (ProductPropertyRelationVo property : vo.getProperties()) {
        ProductProperty productProperty = productPropertyService.findById(property.getId());
        if (productProperty == null) {
          throw new DefaultClientException("商品属性不存在！");
        }
        if (productProperty.getColumnType() == ColumnType.SINGLE) {
          ProductPropertyItem propertyItem = productPropertyItemService.findById(
              property.getText());
          if (propertyItem == null) {
            throw new DefaultClientException("商品属性值不存在！");
          }

          CreateProductPropertyRelationVo createProductPropertyRelationVo = new CreateProductPropertyRelationVo();
          createProductPropertyRelationVo.setProductId(data.getId());
          createProductPropertyRelationVo.setPropertyId(productProperty.getId());
          createProductPropertyRelationVo.setPropertyItemId(propertyItem.getId());

          productPropertyRelationService.create(createProductPropertyRelationVo);
        } else if (productProperty.getColumnType() == ColumnType.MULTIPLE) {

          List<String> propertyItemIds = JsonUtil.parseList(property.getText(), String.class);
          for (String propertyItemId : propertyItemIds) {
            CreateProductPropertyRelationVo createProductPropertyRelationVo = new CreateProductPropertyRelationVo();
            createProductPropertyRelationVo.setProductId(data.getId());
            createProductPropertyRelationVo.setPropertyId(productProperty.getId());
            createProductPropertyRelationVo.setPropertyItemId(propertyItemId);

            productPropertyRelationService.create(createProductPropertyRelationVo);
          }

        } else if (productProperty.getColumnType() == ColumnType.CUSTOM) {

          CreateProductPropertyRelationVo createProductPropertyRelationVo = new CreateProductPropertyRelationVo();
          createProductPropertyRelationVo.setProductId(data.getId());
          createProductPropertyRelationVo.setPropertyId(productProperty.getId());
          createProductPropertyRelationVo.setPropertyText(property.getText());
          productPropertyRelationService.create(createProductPropertyRelationVo);
        } else {
          throw new DefaultClientException("商品属性字段类型不存在！");
        }
      }
    }

    return data.getId();
  }

  /**
   * 创建多规格商品
   * @param vo
   */
  private String createProductSpec(CreateProductVo vo) {
      String shopId = vo.getShopId();

      Wrapper<Product> checkWrapper = Wrappers.lambdaQuery(Product.class)
              .eq(Product::getCode, vo.getCode())
              .eq(StrUtil.isNotEmpty(shopId), Product::getShopId, shopId)
              .isNull(StrUtil.isBlank(shopId), Product::getShopId);
      if (getBaseMapper().selectCount(checkWrapper) > 0) {
          throw new DefaultClientException("编号重复，请重新输入！");
      }

      checkWrapper = Wrappers.lambdaQuery(Product.class).eq(Product::getSkuCode, vo.getSkuCode())
              .eq(StrUtil.isNotEmpty(shopId), Product::getShopId, shopId)
              .isNull(StrUtil.isBlank(shopId), Product::getShopId);
      if (getBaseMapper().selectCount(checkWrapper) > 0) {
          throw new DefaultClientException("商品SKU编号重复，请重新输入！");
      }

      if (!StrUtil.isNotEmpty(shopId)) {
          vo.setShopId(null);
      }

      if (StrUtil.isBlank(vo.getSkuCode())) {
          vo.setSkuCode(getThis(GenerateCodeService.class).generate(99));
      }
      vo.setPurchasePrice(BigDecimal.ZERO);
      vo.setSalePrice(BigDecimal.ZERO);
      vo.setRetailPrice(BigDecimal.ZERO);

      Product data = new Product();
      data.setId(IdUtil.getId());
      data.setCode(vo.getCode());
      data.setName(vo.getName());
      if (StringUtil.isNotBlank(vo.getShortName())) {
          data.setShortName(vo.getShortName());
      }
      data.setSkuCode(vo.getSkuCode());
      if (StringUtil.isNotBlank(vo.getExternalCode())) {
          data.setExternalCode(vo.getExternalCode());
      }

      data.setBrandId(vo.getBrandId());
      data.setCategoryId(vo.getCategoryId());

      if (StringUtil.isNotBlank(vo.getSpec())) {
          data.setSpec(vo.getSpec());
      }

      if (StringUtil.isNotBlank(vo.getUnit())) {
          data.setUnit(vo.getUnit());
      }

      if (StringUtil.isNotBlank(vo.getStockUnit())) {
          data.setStockUnit(vo.getStockUnit());
      }

      if (StringUtil.isNotBlank(vo.getSalesUnit())) {
          data.setSalesUnit(vo.getSalesUnit());
      }

      if (StringUtil.isNotBlank(vo.getRetailUnit())) {
          data.setRetailUnit(vo.getRetailUnit());
      }

      data.setProductType(EnumUtil.getByCode(ProductType.class, vo.getProductType()));
      data.setTaxRate(vo.getTaxRate());
      data.setSaleTaxRate(vo.getSaleTaxRate());
      data.setWeight(vo.getWeight());
      data.setVolume(vo.getVolume());

      data.setAvailable(Boolean.TRUE);
      data.setImageUrl(vo.getImageUrl());
      data.setIntroduction(vo.getIntroduction());
      data.setExpiryDay(vo.getExpiryDay());
      data.setShopId(vo.getShopId());

      save(data);

      saveProductPurchasePrice(data, vo.getPurchasePrice());
      saveProductSalePrice(data, vo.getSalePrice());
      saveProductRetailPrice(data, vo.getRetailPrice());

      saveProductPropertyRelation(data, vo.getProperties());
      List<ProductSpecSaveVo> productData = vo.getProductData();
      if (CollUtil.isNotEmpty(productData)) {
        for (ProductSpecSaveVo productSpecSaveVo : productData) {
            Product specProduct = saveSpecProduct(data, productSpecSaveVo);
            saveProductPurchasePrice(specProduct, productSpecSaveVo.getPurchasePrice());
            saveProductSalePrice(specProduct, productSpecSaveVo.getSalePrice());
            saveProductRetailPrice(specProduct, productSpecSaveVo.getRetailPrice());
            saveProductProperty(specProduct, productSpecSaveVo.getProperty());
        }
      }

      return data.getId();
  }

  @OpLog(type = BaseDataOpLogType.class, name = "修改商品，ID：{}, 编号：{}", params = {"#id", "#code"})
  @Transactional(rollbackFor = Exception.class)
  @Override
  public void update(UpdateProductVo vo) {

    Product data = getBaseMapper().selectById(vo.getId());
    if (ObjectUtil.isNull(data)) {
      throw new DefaultClientException("商品不存在！");
    }

    String shopId = vo.getShopId();


    Wrapper<Product> checkWrapper = Wrappers.lambdaQuery(Product.class)
        .eq(Product::getCode, vo.getCode()).isNull(Product::getMainProductId)
        .ne(Product::getId, vo.getId())
        .eq(StrUtil.isNotEmpty(shopId), Product::getShopId, shopId)
        .isNull(StrUtil.isBlank(shopId), Product::getShopId);
    if (getBaseMapper().selectCount(checkWrapper) > 0) {
      throw new DefaultClientException("编号重复，请重新输入！");
    }

    checkWrapper = Wrappers.lambdaQuery(Product.class).eq(Product::getSkuCode, vo.getSkuCode())
        .ne(Product::getId, vo.getId())
        .eq(StrUtil.isNotEmpty(shopId), Product::getShopId, shopId)
        .isNull(StrUtil.isBlank(shopId), Product::getShopId);;
    if (getBaseMapper().selectCount(checkWrapper) > 0) {
      throw new DefaultClientException("商品SKU编号重复，请重新输入！");
    }

    if (!StrUtil.isNotBlank(shopId)) {
        vo.setShopId(null);
    }

    if (vo.getProductType() == 3) {
        updateProductSpec(vo);
        return;
    }

    LambdaUpdateWrapper<Product> updateWrapper = Wrappers.lambdaUpdate(Product.class)
        .set(Product::getCode, vo.getCode()).set(Product::getName, vo.getName())
        .set(Product::getAvailable, vo.getAvailable()).set(Product::getSkuCode, vo.getSkuCode())
        .set(Product::getExternalCode,
            StringUtil.isBlank(vo.getExternalCode()) ? null : vo.getExternalCode())
        .set(Product::getSpec, StringUtil.isBlank(vo.getSpec()) ? null : vo.getSpec())
        .set(Product::getUnit, StringUtil.isBlank(vo.getUnit()) ? null : vo.getUnit())
        .set(Product::getStockUnit, StringUtil.isBlank(vo.getStockUnit()) ? null : vo.getStockUnit())
        .set(Product::getSalesUnit, StringUtil.isBlank(vo.getSalesUnit()) ? null : vo.getSalesUnit())
        .set(Product::getRetailUnit, StringUtil.isBlank(vo.getRetailUnit()) ? null : vo.getRetailUnit())
        .set(Product::getShortName,
            StringUtil.isBlank(vo.getShortName()) ? null : vo.getShortName())
        .set(Product::getCategoryId,
            StringUtil.isBlank(vo.getCategoryId()) ? null : vo.getCategoryId())
        .set(Product::getBrandId, StringUtil.isBlank(vo.getBrandId()) ? null : vo.getBrandId())
        .set(Product::getTaxRate, vo.getTaxRate())
        .set(Product::getSaleTaxRate, vo.getSaleTaxRate())
        .set(Product::getWeight, vo.getWeight())
        .set(Product::getVolume, vo.getVolume())
        .set(StringUtil.isNotBlank(vo.getImageUrl()), Product::getImageUrl, vo.getImageUrl())
        .set(Product::getIntroduction, vo.getIntroduction())
        .set(Product::getExpiryDay, vo.getExpiryDay())
        .set(Product::getShopId, vo.getShopId())
        .set(Product::getUpdateTime, DateUtil.date().toLocalDateTime())
        .eq(Product::getId, vo.getId());

    getBaseMapper().update(updateWrapper);

    // 组合商品
    if (data.getProductType() == ProductType.BUNDLE) {
      if (CollectionUtil.isEmpty(vo.getProductBundles())) {
        throw new DefaultClientException("单品数据不能为空！");
      }

      BigDecimal salePrice = vo.getProductBundles().stream().map(
          productBundleVo -> NumberUtil.mul(productBundleVo.getBundleNum(),
              productBundleVo.getSalePrice())).reduce(NumberUtil::add).orElse(BigDecimal.ZERO);
      if (!NumberUtil.equal(vo.getSalePrice(), salePrice)) {
        throw new DefaultClientException("单品的销售价设置错误！");
      }

      BigDecimal retailPrice = vo.getProductBundles().stream().map(
          productBundleVo -> NumberUtil.mul(productBundleVo.getBundleNum(),
              productBundleVo.getRetailPrice())).reduce(NumberUtil::add).orElse(BigDecimal.ZERO);
      if (!NumberUtil.equal(vo.getRetailPrice(), retailPrice)) {
        throw new DefaultClientException("单品的零售价设置错误！");
      }

      Wrapper<ProductBundle> deleteBundleWrapper = Wrappers.lambdaQuery(ProductBundle.class)
          .eq(ProductBundle::getMainProductId, data.getId());
      productBundleService.remove(deleteBundleWrapper);

      List<ProductBundle> productBundles = vo.getProductBundles().stream().map(productBundleVo -> {
        ProductBundle productBundle = new ProductBundle();
        productBundle.setId(IdUtil.getId());
        productBundle.setMainProductId(data.getId());
        productBundle.setProductId(productBundleVo.getProductId());
        productBundle.setBundleNum(productBundleVo.getBundleNum());
        productBundle.setSalePrice(productBundleVo.getSalePrice());
        productBundle.setRetailPrice(productBundleVo.getRetailPrice());

        return productBundle;
      }).collect(Collectors.toList());

      productBundleService.saveBatch(productBundles);
    }

    productPropertyRelationService.deleteByProductId(data.getId());
    if (!CollectionUtil.isEmpty(vo.getProperties())) {
      // 商品和商品属性的关系
      for (ProductPropertyRelationVo property : vo.getProperties()) {
        ProductProperty productProperty = productPropertyService.findById(property.getId());
        if (productProperty == null) {
          throw new DefaultClientException("商品属性不存在！");
        }
        if (productProperty.getColumnType() == ColumnType.SINGLE) {
          ProductPropertyItem propertyItem = productPropertyItemService.findById(
              property.getText());
          if (propertyItem == null) {
            throw new DefaultClientException("商品属性值不存在！");
          }

          CreateProductPropertyRelationVo createProductPropertyRelationVo = new CreateProductPropertyRelationVo();
          createProductPropertyRelationVo.setProductId(data.getId());
          createProductPropertyRelationVo.setPropertyId(productProperty.getId());
          createProductPropertyRelationVo.setPropertyItemId(propertyItem.getId());

          productPropertyRelationService.create(createProductPropertyRelationVo);
        } else if (productProperty.getColumnType() == ColumnType.MULTIPLE) {

          List<String> propertyItemIds = JsonUtil.parseList(property.getText(), String.class);
          for (String propertyItemId : propertyItemIds) {
            CreateProductPropertyRelationVo createProductPropertyRelationVo = new CreateProductPropertyRelationVo();
            createProductPropertyRelationVo.setProductId(data.getId());
            createProductPropertyRelationVo.setPropertyId(productProperty.getId());
            createProductPropertyRelationVo.setPropertyItemId(propertyItemId);

            productPropertyRelationService.create(createProductPropertyRelationVo);
          }

        } else if (productProperty.getColumnType() == ColumnType.CUSTOM) {

          CreateProductPropertyRelationVo createProductPropertyRelationVo = new CreateProductPropertyRelationVo();
          createProductPropertyRelationVo.setProductId(data.getId());
          createProductPropertyRelationVo.setPropertyId(productProperty.getId());
          createProductPropertyRelationVo.setPropertyText(property.getText());
          productPropertyRelationService.create(createProductPropertyRelationVo);
        } else {
          throw new DefaultClientException("商品属性字段类型不存在！");
        }
      }
    }

    productPurchaseService.removeById(data.getId());

    if (vo.getPurchasePrice() != null) {

      UpdateProductPurchaseVo updateProductPurchaseVo = new UpdateProductPurchaseVo();
      updateProductPurchaseVo.setId(data.getId());
      updateProductPurchaseVo.setPrice(vo.getPurchasePrice());

      productPurchaseService.update(updateProductPurchaseVo);
    }

    productSaleService.removeById(data.getId());

    if (vo.getSalePrice() != null) {
      UpdateProductSaleVo updateProductSaleVo = new UpdateProductSaleVo();
      updateProductSaleVo.setId(data.getId());
      updateProductSaleVo.setPrice(vo.getSalePrice());

      productSaleService.update(updateProductSaleVo);
    }

    productRetailService.removeById(data.getId());
    if (vo.getRetailPrice() != null) {
      UpdateProductRetailVo updateProductRetailVo = new UpdateProductRetailVo();
      updateProductRetailVo.setId(data.getId());
      updateProductRetailVo.setPrice(vo.getRetailPrice());

      productRetailService.update(updateProductRetailVo);
    }

    if(vo.getProductType() == 1) {
        updateUnitCostPrice(data.getId());
    }

    OpLogUtil.setVariable("id", data.getId());
    OpLogUtil.setVariable("code", vo.getCode());
    OpLogUtil.setExtra(vo);
  }

  /**
   * 更新多规格商品
   * @param vo
   */
  private void updateProductSpec(UpdateProductVo vo) {
      boolean updateRes = lambdaUpdate().eq(Product::getId, vo.getId())
              .set(Product::getCode, vo.getCode()).set(Product::getName, vo.getName())
              .set(Product::getAvailable, vo.getAvailable()).set(Product::getSkuCode, vo.getSkuCode())
              .set(Product::getExternalCode,
                      StringUtil.isBlank(vo.getExternalCode()) ? null : vo.getExternalCode())
              .set(Product::getSpec, StringUtil.isBlank(vo.getSpec()) ? null : vo.getSpec())
              .set(Product::getUnit, StringUtil.isBlank(vo.getUnit()) ? null : vo.getUnit())
              .set(Product::getStockUnit, StringUtil.isBlank(vo.getStockUnit()) ? null : vo.getStockUnit())
              .set(Product::getSalesUnit, StringUtil.isBlank(vo.getSalesUnit()) ? null : vo.getSalesUnit())
              .set(Product::getRetailUnit, StringUtil.isBlank(vo.getRetailUnit()) ? null : vo.getRetailUnit())
              .set(Product::getShortName,
                      StringUtil.isBlank(vo.getShortName()) ? null : vo.getShortName())
              .set(Product::getCategoryId,
                      StringUtil.isBlank(vo.getCategoryId()) ? null : vo.getCategoryId())
              .set(Product::getBrandId, StringUtil.isBlank(vo.getBrandId()) ? null : vo.getBrandId())
              .set(Product::getTaxRate, vo.getTaxRate())
              .set(Product::getSaleTaxRate, vo.getSaleTaxRate())
              .set(Product::getWeight, vo.getWeight())
              .set(Product::getVolume, vo.getVolume())
              .set(StringUtil.isNotBlank(vo.getImageUrl()), Product::getImageUrl, vo.getImageUrl())
              .set(Product::getIntroduction, vo.getIntroduction())
              .set(Product::getExpiryDay, vo.getExpiryDay())
              .set(Product::getUpdateTime, DateUtil.date().toLocalDateTime())
              .set(Product::getShopId, vo.getShopId())
              .update();
      if (updateRes) {
          Product data = lambdaQuery().eq(Product::getId, vo.getId()).one();

          saveProductPurchasePrice(data, vo.getPurchasePrice());
          saveProductSalePrice(data, vo.getSalePrice());
          saveProductRetailPrice(data, vo.getRetailPrice());

          saveProductPropertyRelation(data, vo.getProperties());
          List<ProductSpecSaveVo> productData = vo.getProductData();
          if (CollUtil.isNotEmpty(productData)) {
              for (ProductSpecSaveVo productSpecSaveVo : productData) {
                  Product specProduct = saveSpecProduct(data, productSpecSaveVo);
                  saveProductPurchasePrice(specProduct, productSpecSaveVo.getPurchasePrice());
                  saveProductSalePrice(specProduct, productSpecSaveVo.getSalePrice());
                  saveProductRetailPrice(specProduct, productSpecSaveVo.getRetailPrice());
                  saveProductProperty(specProduct, productSpecSaveVo.getProperty());
              }
          }

          updateUnitCostPrice(vo.getId());
      }

      OpLogUtil.setVariable("id", vo.getId());
      OpLogUtil.setVariable("code", vo.getCode());
      OpLogUtil.setExtra(vo);
  }

  @Override
  public List<Product> getByCategoryIds(List<String> categoryIds, Integer productType) {

    if (CollectionUtil.isEmpty(categoryIds)) {
      return CollectionUtil.emptyList();
    }

    // 根据categoryIds查询所有叶子节点
    List<String> children = new ArrayList<>();
    for (String categoryId : categoryIds) {
      children.addAll(recursionMappingService.getNodeChildIds(categoryId,
          ProductCategoryNodeType.class));
    }

    children.addAll(categoryIds);

    children = children.stream().distinct().collect(Collectors.toList());

    List<Product> datas = getBaseMapper().getByCategoryIds(children, productType);

    return datas;
  }

  @Override
  public List<Product> getByBrandIds(List<String> brandIds, Integer productType) {

    if (CollectionUtil.isEmpty(brandIds)) {
      return CollectionUtil.emptyList();
    }

    return getBaseMapper().getByBrandIds(brandIds, productType);
  }

  @Override
  public ProductInfoBo selectByBo(String productId) {
      return Optional.ofNullable(getBaseMapper().selectByBo(Collections.singletonList(productId)))
              .map(item -> CollUtil.isNotEmpty(item) ? item.get(0) : null)
              .orElse(null);
  }

  @Override
  public List<ProductInfoBo> selectByBo(List<String> productIds) {
      return getBaseMapper().selectByBo(productIds);
  }

  @Override
  public List<Product> getSpecProductByCode(String code) {
      return getBaseMapper().selectSpecProductByCode(code);
  }

  @Override
  public List<ProductSpecDto> getSpecProductDtoByMainProductId(String productId) {
      return getBaseMapper().selectSpecProductDtoByMainProductId(productId);
  }

  @Override
  public List<Unit> getUnitByProductId(String productId) {
    if (StrUtil.isBlank(productId)) {
        return Collections.emptyList();
    } else {
      return getBaseMapper().getUnitByProductId(productId);
    }
  }

  @Override
  public BigDecimal calcCost(String productId, String unitId, BigDecimal num) {
      // 商品信息
      Product product = lambdaQuery().eq(Product::getId, productId)
              .one();
      if (Objects.nonNull(product)) {
          // 商品采购价
          ProductPurchase productPurchase = productPurchaseService.lambdaQuery()
                  .eq(ProductPurchase::getId, product.getId())
                  .one();
          if (Objects.nonNull(productPurchase)) {
              // 单位成本价集合
              Map<String, BigDecimal> unitCostPriceMap = new HashMap<>();
              // 基础单位成本价
              unitCostPriceMap.put(product.getUnit(), productPurchase.getPrice());
              // 查询单位转换规则
              Map<String, ProductAssistantUnit> assistantUniMap = productAssistantUnitService.lambdaQuery()
                      .eq(ProductAssistantUnit::getProductId, product.getId()).list()
                      .stream().collect(Collectors.toMap(ProductAssistantUnit::getToUnitId, Function.identity()));

              // 单位成本价
              BigDecimal unitCostPrice = getUnitCostPrice(assistantUniMap, unitCostPriceMap, unitId);

              if(Objects.isNull(unitCostPrice)) {
                  throw new DefaultClientException("单位未能匹配成本");
              }

              // 成本价 = 单位成本价 * 数量
              return unitCostPrice.multiply(num);
          }
      }
      return BigDecimal.ZERO;
  }

  /**
   * 获取单位成本价
   * @param assistantUniMap 单位转换规则
   * @param unitCostPriceMap 单位成本价集合
   * @param unitId 单位
   * @return
   */
  private BigDecimal getUnitCostPrice(Map<String, ProductAssistantUnit> assistantUniMap,
                             Map<String, BigDecimal> unitCostPriceMap,
                             String unitId) {
    if (Objects.isNull(unitCostPriceMap)) {
        throw new DefaultClientException("单位未配置转换规则");
    }
    // 集合中不存在单位对应的成本价
    if (!unitCostPriceMap.containsKey(unitId)) {
        // 获取单位的转换记录
        ProductAssistantUnit productAssistantUnit = assistantUniMap.get(unitId);
        if (Objects.isNull(productAssistantUnit)) {
            throw new DefaultClientException("单位没有有效转换规则");
        }
        // 库存单位
        String assistantUnitId = productAssistantUnit.getAssistantUnitId();
        // 获取库存单位的成本价
        BigDecimal assistantUnitCostPrice = getUnitCostPrice(assistantUniMap, unitCostPriceMap, assistantUnitId);
        // 计算当前单位成本价 (库存单位 / 数量)
        BigDecimal unitCostPrice = assistantUnitCostPrice.divide(productAssistantUnit.getUnitVal(), 6, RoundingMode.HALF_UP);
        // 添加至单位成本价集合中
        unitCostPriceMap.put(unitId, unitCostPrice);
    }
    return unitCostPriceMap.get(unitId);
  }

  /**
   * 更新做法里消耗成本
   * @param productId
   */
  private void updateUnitCostPrice(String productId) {
      Product product = getBaseMapper().findById(productId);
      if (Objects.nonNull(product) && Arrays.asList(1, 4).contains(product.getProductType().getCode())) {
          List<MakeMethodMaterial> makeMethodMaterials = makeMethodMaterialService.lambdaQuery()
                  .eq(MakeMethodMaterial::getCode, product.getCode())
                  .eq(MakeMethodMaterial::getSkuCode, product.getSkuCode())
                  .list();
          for (MakeMethodMaterial makeMethodMaterial : makeMethodMaterials) {
              try {
                  BigDecimal unitCostPrice = calcCost(product.getId(), makeMethodMaterial.getConsumeUnitId(), makeMethodMaterial.getConsumeNum());
                  makeMethodMaterialService.lambdaUpdate()
                          .set(MakeMethodMaterial::getConsumeCost, unitCostPrice)
                          .eq(MakeMethodMaterial::getId, makeMethodMaterial.getId());
              } catch (Exception ignored) {}
          }
      }
  }

  @CacheEvict(value = Product.CACHE_NAME, key = "@cacheVariables.tenantId() + #key")
  @Override
  public void cleanCacheByKey(Serializable key) {

  }

  /**
   * 保存商品自定义属性
   * @param mainProduct
   * @param propertyList
   */
  private void saveProductPropertyRelation(Product mainProduct, List<ProductPropertyRelationVo> propertyList) {
      if (Objects.nonNull(mainProduct) && CollUtil.isNotEmpty(propertyList)) {
          for (ProductPropertyRelationVo property : propertyList) {
              ProductProperty productProperty = productPropertyService.findById(property.getId());
              if (productProperty == null) {
                  throw new DefaultClientException("编码：" + mainProduct.getCode() + ",商品属性不存在！");
              }
              if (productProperty.getColumnType() == ColumnType.SINGLE) {
                  boolean updateRes = productPropertyRelationService.lambdaUpdate()
                          .eq(ProductPropertyRelation::getProductId, mainProduct.getId())
                          .eq(ProductPropertyRelation::getPropertyId, productProperty.getId())
                          .set(ProductPropertyRelation::getPropertyItemId, property.getText())
                          .update();
                  if (!updateRes) {
                      ProductPropertyRelation productPropertyRelation = new ProductPropertyRelation();
                      productPropertyRelation.setProductId(mainProduct.getId());
                      productPropertyRelation.setPropertyId(productProperty.getId());
                      productPropertyRelation.setPropertyItemId(property.getText());
                      productPropertyRelationService.save(productPropertyRelation);
                  }
              }
              else if (productProperty.getColumnType() == ColumnType.MULTIPLE) {
                  List<String> propertyItemIds = JsonUtil.parseList(property.getText(), String.class);
                  productPropertyRelationService.lambdaUpdate()
                          .eq(ProductPropertyRelation::getProductId, mainProduct.getId())
                          .eq(ProductPropertyRelation::getPropertyId, productProperty.getId())
                          .remove();
                  for (String propertyItemId : propertyItemIds) {
                      ProductPropertyRelation productPropertyRelation = new ProductPropertyRelation();
                      productPropertyRelation.setProductId(mainProduct.getId());
                      productPropertyRelation.setPropertyId(productProperty.getId());
                      productPropertyRelation.setPropertyItemId(propertyItemId);
                      productPropertyRelationService.save(productPropertyRelation);
                  }
              }
              else if (productProperty.getColumnType() == ColumnType.CUSTOM) {
                  boolean updateRes = productPropertyRelationService.lambdaUpdate()
                          .eq(ProductPropertyRelation::getProductId, mainProduct.getId())
                          .eq(ProductPropertyRelation::getPropertyId, productProperty.getId())
                          .set(ProductPropertyRelation::getPropertyText, property.getText())
                          .update();
                  if (!updateRes) {
                      ProductPropertyRelation productPropertyRelation = new ProductPropertyRelation();
                      productPropertyRelation.setProductId(mainProduct.getId());
                      productPropertyRelation.setPropertyId(productProperty.getId());
                      productPropertyRelation.setPropertyText(property.getText());
                      productPropertyRelationService.save(productPropertyRelation);
                  }
              }
          }
      }
  }

  /**
   * 保存规格商品
   * @param mainProduct
   * @param vo
   * @return 规格商品
   */
  private Product saveSpecProduct(Product mainProduct, ProductSpecSaveVo vo) {
      if (Objects.nonNull(mainProduct) && Objects.nonNull(vo)) {
          LambdaUpdateChainWrapper<Product> lambdaUpdate = lambdaUpdate();
          boolean updateRes = lambdaUpdate.eq(Product::getMainProductId, mainProduct.getId())
                  .eq(Product::getId, vo.getSpecId())
                  .set(Product::getName, mainProduct.getName())
                  .set(Product::getShortName, mainProduct.getShortName())
                  .set(Product::getExternalCode, mainProduct.getExternalCode())
                  .set(Product::getCategoryId, mainProduct.getCategoryId())
                  .set(Product::getBrandId, mainProduct.getBrandId())
                  .set(Product::getTaxRate, mainProduct.getTaxRate())
                  .set(Product::getSaleTaxRate, mainProduct.getSaleTaxRate())
                  .set(Product::getImageUrl, mainProduct.getImageUrl())
                  .set(Product::getExpiryDay, mainProduct.getExpiryDay())
                  .set(Product::getIntroduction, mainProduct.getIntroduction())
                  .set(Product::getUnit, mainProduct.getUnit())
                  .set(Product::getShopId, mainProduct.getShopId())
                  .set(Product::getSpec, vo.getSpec())
                  .set(Product::getWeight, vo.getWeight())
                  .set(Product::getVolume, vo.getVolume())
                  .set(Product::getAvailable, vo.getAvailable())
                  .set(Product::getValidPropertyValues, vo.getValidPropertyValues())
                  .set(Product::getUpdateTime, DateUtil.date().toLocalDateTime())
                  .update();
          if (!updateRes) {
              Product specProduct = new Product();
              specProduct.setId(IdUtil.getId());
              specProduct.setMainProductId(mainProduct.getId());
              specProduct.setCode(mainProduct.getCode());
              specProduct.setName(mainProduct.getName());
              specProduct.setExternalCode(mainProduct.getExternalCode());
              specProduct.setBrandId(mainProduct.getBrandId());
              specProduct.setCategoryId(mainProduct.getCategoryId());
              specProduct.setProductType(ProductType._SPEC);
              specProduct.setTaxRate(mainProduct.getTaxRate());
              specProduct.setSaleTaxRate(mainProduct.getSaleTaxRate());
              specProduct.setImageUrl(mainProduct.getImageUrl());
              specProduct.setIntroduction(mainProduct.getIntroduction());
              specProduct.setExpiryDay(mainProduct.getExpiryDay());
              specProduct.setShopId(mainProduct.getShopId());
              specProduct.setSkuCode(vo.getSkuCode());
              specProduct.setSpec(vo.getSpec());
              specProduct.setUnit(mainProduct.getUnit());
              specProduct.setWeight(vo.getWeight());
              specProduct.setVolume(vo.getVolume());
              specProduct.setAvailable(vo.getAvailable());
              specProduct.setValidPropertyValues(vo.getValidPropertyValues());
              save(specProduct);
              return specProduct;
          } else {
              return lambdaQuery().eq(Product::getId, vo.getSpecId()).one();
          }
      }
      return null;
  }

  /**
   * 保存商品采购价
   * @param product
   * @param purchasePrice
   */
  private void saveProductPurchasePrice(Product product, BigDecimal purchasePrice) {
      if (Objects.nonNull(product)) {
          if (Objects.isNull(purchasePrice)) {
              throw new DefaultClientException( "SKU编码：" + product.getSkuCode() + ",采购价不能为空！");
          }
          if (purchasePrice.doubleValue() < 0D) {
              throw new DefaultClientException( "SKU编码：" + product.getSkuCode() + ",采购价不允许小于0！");
          }
          LambdaUpdateChainWrapper<ProductPurchase> lambdaUpdate = productPurchaseService.lambdaUpdate();
          boolean updateRes = lambdaUpdate.eq(ProductPurchase::getId, product.getId())
                  .set(ProductPurchase::getPrice, purchasePrice)
                  .update();
          if (!updateRes) {
              ProductPurchase productPurchase = new ProductPurchase();
              productPurchase.setId(product.getId());
              productPurchase.setPrice(purchasePrice);
              productPurchaseService.save(productPurchase);
          }
      }
  }

  /**
   * 保存商品销售价
   * @param product
   * @param salePrice
   */
  private void saveProductSalePrice(Product product, BigDecimal salePrice) {
      if (Objects.nonNull(product)) {
          if (Objects.isNull(salePrice)) {
              throw new DefaultClientException( "SKU编码：" + product.getSkuCode() + ",销售价不能为空！");
          }
          if (salePrice.doubleValue() < 0D) {
              throw new DefaultClientException( "SKU编码：" + product.getSkuCode() + ",销售价不允许小于0！");
          }
          LambdaUpdateChainWrapper<ProductSale> lambdaUpdate = productSaleService.lambdaUpdate();
          boolean updateRes = lambdaUpdate.eq(ProductSale::getId, product.getId())
                  .set(ProductSale::getPrice, salePrice)
                  .update();
          if (!updateRes) {
              ProductSale productSale = new ProductSale();
              productSale.setId(product.getId());
              productSale.setPrice(salePrice);
              productSaleService.save(productSale);
          }
      }
  }

  /**
   * 保存商品零售价
   * @param product
   * @param retailPrice
   */
  private void saveProductRetailPrice(Product product, BigDecimal retailPrice) {
      if (Objects.nonNull(product)) {
          if (Objects.isNull(retailPrice)) {
              throw new DefaultClientException( "SKU编码：" + product.getSkuCode() + ",零售价不能为空！");
          }

          if (retailPrice.doubleValue() < 0D) {
              throw new DefaultClientException( "SKU编码：" + product.getSkuCode() + ",零售价不允许小于0！");
          }
          LambdaUpdateChainWrapper<ProductRetail> lambdaUpdate = productRetailService.lambdaUpdate();
          boolean updateRes = lambdaUpdate.eq(ProductRetail::getId, product.getId())
                  .set(ProductRetail::getPrice, retailPrice)
                  .update();
          if (!updateRes) {
              ProductRetail productRetail = new ProductRetail();
              productRetail.setId(product.getId());
              productRetail.setPrice(retailPrice);
              productRetailService.save(productRetail);
          }
      }
  }

  /**
   * 保存商品规格值
   * @param product
   * @param propertyList
   */
  private void saveProductProperty(Product product, List<ProductSpecSaveVo.PropertyVo> propertyList) {
      if (Objects.nonNull(product) && CollUtil.isNotEmpty(propertyList)) {
          for (ProductSpecSaveVo.PropertyVo property : propertyList) {
              if (!productPropertyService.lambdaQuery().eq(ProductProperty::getId, property.getPropertyId()).exists()) {
                  throw new DefaultClientException("SKU编码：" + product.getSkuCode() + ",商品属性不存在！");
              }
              if (!productPropertyItemService.lambdaQuery().eq(ProductPropertyItem::getId, property.getPropertyItemId()).exists()) {
                  throw new DefaultClientException("SKU编码：" + product.getSkuCode() + ",商品属性值不存在！");
              }
              LambdaUpdateChainWrapper<ProductPropertyRelation> lambdaUpdate = productPropertyRelationService
                      .lambdaUpdate();
              boolean updateRes = lambdaUpdate.eq(ProductPropertyRelation::getProductId, product.getId())
                      .eq(ProductPropertyRelation::getPropertyId, property.getPropertyId())
                      .set(ProductPropertyRelation::getPropertyItemId, property.getPropertyItemId())
                      .update();
              if (!updateRes) {
                  ProductPropertyRelation productPropertyRelation = new ProductPropertyRelation();
                  productPropertyRelation.setProductId(product.getId());
                  productPropertyRelation.setPropertyId(property.getPropertyId());
                  productPropertyRelation.setPropertyItemId(property.getPropertyItemId());
                  productPropertyRelationService.save(productPropertyRelation);
              }
          }
      }
  }
}
